﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Mindfor.Web.Config;
using Mindfor.Web.Data;
using System.Reflection;
using System.Web.Routing;

namespace Mindfor.Web
{
	/// <summary>
	/// Provides extension functions for <see cref="UrlHelper"/>.
	/// </summary>
	public static class UrlHelperExtensions
	{
		#region Module

		/// <summary>
		/// Generates a fully qualified URL to an action method by using the specified action name, controller name, and route values.
		/// </summary>
		/// <param name="actionName">The name of the action method.</param>
		/// <param name="controllerName">The name of the controller.</param>
		/// <param name="moduleName">The name of the module.</param>
		/// <returns>The fully qualified URL to an action method.</returns>
		public static string ActionModule(this UrlHelper url, string actionName, string controllerName, string moduleName)
		{
			return url.Action(actionName, controllerName, new { module = moduleName });
		}

		/// <summary>
		/// Generates a fully qualified URL to an action method by using the specified action name, controller name, and route values.
		/// </summary>
		/// <param name="actionName">The name of the action method.</param>
		/// <param name="controllerName">The name of the controller.</param>
		/// <param name="moduleName">The name of the module.</param>
		/// <param name="routeValues">
		/// An object that contains the parameters for a route. The parameters are retrieved
		/// through reflection by examining the properties of the object. The object
		/// is typically created by using object initializer syntax.
		/// </param>
		/// <returns>The fully qualified URL to an action method.</returns>
		public static string ActionModule(this UrlHelper url, string actionName, string controllerName, string moduleName, object routeValues)
		{
			if (routeValues == null)
				return ActionModule(url, actionName, controllerName, moduleName);
			RouteValueDictionary values = new RouteValueDictionary(routeValues);
			values["module"] = moduleName;
			return url.Action(actionName, controllerName, values);
		}

		#endregion
		
		#region Page

		static Dictionary<int, List<Route>> s_pageRoutes = new Dictionary<int, List<Route>>();

		/// <summary>
		/// Returns url to page.
		/// </summary>
		/// <param name="url">UrlHelper.</param>
		/// <param name="pageId">Page id to get url to.</param>
		/// <param name="routeValues">Dictionary that contains parameters for the route.</param>
		/// <returns>Url to page.</returns>
		public static string Page(this UrlHelper url, int pageId, RouteValueDictionary routeValues)
		{
			if (routeValues == null)
				routeValues = new RouteValueDictionary();

			// get routes with page id
			List<Route> pageRoutes;
			if (s_pageRoutes.ContainsKey(pageId))
				pageRoutes = s_pageRoutes[pageId];
			else
			{
				// collect routes with page id
				pageRoutes = new List<Route>();
				s_pageRoutes.Add(pageId, pageRoutes);
				foreach (RouteBase routeBase in RouteTable.Routes)
				{
					if (routeBase is Route)
					{
						Route route = (Route)routeBase;
						if (route.Defaults != null && route.Defaults["pageId"].To<int>() == pageId)
							pageRoutes.Add(route);
					}
				}
			}

			// select match route
			foreach (Route route in pageRoutes)
			{
				VirtualPathData pathData = route.GetVirtualPath(url.RequestContext, routeValues);
				if (pathData != null)
					return pathData.VirtualPath.StartsWith("/") ? pathData.VirtualPath : "/" + pathData.VirtualPath;
			}

			// no route found
			return null;
		}

		/// <summary>
		/// Returns url to page.
		/// </summary>
		/// <param name="url">UrlHelper.</param>
		/// <param name="pageId">Page id to get url to.</param>
		/// <param name="routeValues">Object that contains parameters for the route.</param>
		/// <returns>Url to page.</returns>
		public static string Page(this UrlHelper url, int pageId, object routeValues)
		{
			RouteValueDictionary routeValuesDic = routeValues == null ? null : new RouteValueDictionary(routeValues);
			return Page(url, pageId, routeValuesDic);
		}

		/// <summary>
		/// Returns url to page.
		/// </summary>
		/// <param name="url">UrlHelper.</param>
		/// <param name="pageId">Page id to get url to.</param>
		/// <returns>Url to page.</returns>
		public static string Page(this UrlHelper url, int pageId)
		{
			return Page(url, pageId, null);
		}

		#endregion

		#region Image

		/// <summary>
		/// Builds url to the image stored in Files table.
		/// </summary>
		/// <param name="url">UrlHelper used to call this method.</param>
		/// <param name="id">Id of the wanted image.</param>
		/// <param name="size">Image max size. If null then source image will be used.</param>
		/// <returns>Url of the image.</returns>
		public static string Image(this UrlHelper url, int id, int? size = null)
		{
			return url.RouteUrl("Image", new { id, ext = "jpg", size });
		}

		/// <summary>
		/// Builds url to the image stored in Files table.
		/// Image is resized to small size (150x150 restricted).
		/// </summary>
		/// <param name="url">UrlHelper used to call this method.</param>
		/// <param name="id">Id of the wanted image.</param>
		/// <returns>Url of the small image.</returns>
		public static string ImageSmall(this UrlHelper url, int id)
		{
			return Image(url, id, 150);
		}

		/// <summary>
		/// Builds url to the image stored in any table.
		/// </summary>
		/// <param name="url">UrlHelper used to call this method.</param>
		/// <param name="entity">Image entity.</param>
		/// <param name="fieldName">Image field name.</param>
		/// <param name="size">Image max size. If null then source image will be used.</param>
		/// <returns>Url of the image.</returns>
		public static string ImageEntity(this UrlHelper url, IdEntity entity, string fieldName, int? size)
		{
			if (entity == null)
				throw new ArgumentNullException("entity");
		    if (String.IsNullOrEmpty(fieldName))
				throw new ArgumentNullException("fieldName");

			if (entity.GetPropertyValue(fieldName) == null)
				return null;
			return url.RouteUrl("ImageEntity", new { typeName = entity.GetType().FullName, id = entity.Id, fieldName, size });
		}

		/// <summary>
		/// Builds url to the image stored in any table.
		/// </summary>
		/// <typeparam name="T">entity type to load from datasource.</typeparam>
		/// <param name="url">UrlHelper used to call this method.</param>
		/// <param name="entity">Image entity.</param>
		/// <param name="fieldName">Image field name.</param>
		/// <param name="size">Image max size. If null then source image will be used.</param>
		/// <returns>Url of the image.</returns>
		public static string ImageEntity<T>(this UrlHelper url, T entity, string fieldName, int? size)
			where T : IdEntity
		{
			if (entity == null)
				throw new ArgumentNullException("entity");
			if (String.IsNullOrEmpty(fieldName))
				throw new ArgumentNullException("fieldName");

			if (entity.GetPropertyValue(fieldName) == null)
				return null;
			return url.RouteUrl("ImageEntity", new { typeName = typeof(T).FullName, id = entity.Id, fieldName, size });
		}

		#endregion

		#region File

		/// <summary>
		/// Returns url to file stored in Files table.
		/// </summary>
		/// <param name="url">UrlHelper.</param>
		/// <param name="file">File download entity.</param>
		/// <returns>File url relative to server.</returns>
		public static string File(this UrlHelper url, IFileDownload file)
		{
			return File(url, file.Id, file.Name, file.Extension);
		}

		/// <summary>
		/// Returns url to file stored in Files table.
		/// </summary>
		/// <param name="url">UrlHelper.</param>
		/// <param name="id">File database identity.</param>
		/// <param name="name">File name for URL.</param>
		/// <param name="extension">File extension for URL.</param>
		/// <returns>File url relative to server.</returns>
		public static string File(this UrlHelper url, int id, string name, string extension)
		{
			if (extension.StartsWith("."))
				extension = extension.Substring(1);
			return url.RouteUrl("File", new { id, ext = extension, name });
		}

		#endregion

		#region Captcha

		/// <summary>
		/// Returns url to captcha with specified key.
		/// </summary>
		/// <param name="url">Url helper.</param>
		/// <param name="key">Captcha key.</param>
		/// <returns>Url to captcha image.</returns>
		public static string Captcha(this UrlHelper url, string key)
		{
			return url.RouteUrl("Captcha", new { key });
		}

		#endregion
	}
}
